"use strict";

const co = require("co");
const cp = require('child_process');
const WebSocket = require('ws');
const schedule = require("node-schedule");

const Lock = require("./Lock.js");

function exec(cmd) {
    return new Promise((resolve, reject) => {
        cp.exec(cmd, function(e, stdout, stderr) {
            if (e) {
                reject(e);
            }
            resolve({
                stderr: stderr,
                stdout: stdout
            });
        });
    });
}
const g = require("./global.js");
const Service = require("./service");

let sleep = function(time) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve();
        }, time);
    });
};
let service;

let sendTo = function(to, data) {
    return new Promise((resolve, reject) => {
        let ws = to;
        if (typeof(to) == "string") {
            ws = g.wsUsers.get(to);
        }
        if (ws && ws.readyState == 1) {
            ws.send(JSON.stringify(data), function(err) {
                if (err) {
                    reject(err);
                }
                resolve();
            });
        } else {
            resolve();
        }
    });
};

let sendData = co.wrap(function*(ws, result) {
    if (result.notifies) {
        for (let notify of result.notifies) {
            yield sendTo(notify.to, notify);
        }
        delete result.notifies;
    }
    if (result.method && result.method != "empty") {
        yield sendTo(ws, result);
    }

});

let bindUnionIdWs = function(unionid, ws) {
    ws.unionid = unionid;
    g.wsUsers.set(unionid, ws);
};

let unbindWs = function(ws) {
    g.wsUsers.delete(ws.unionid);
    delete ws.unionid;
};

let checkParams = co.wrap(function*(timestamp, nonce) {
    const expireTime = 10 * 60 * 1000;
    //如果时间和服务器相差过多
    if (Math.abs(new Date().getTime() - timestamp) > expireTime) {
        return yield Promise.reject({
            errcode: 102,
            entity: "timestamp错误"
        });
    }
    let result = yield service.redisClients.nonceClient.get(nonce);
    if (result) {
        return yield Promise.reject({
            errcode: 103,
            entity: "nonce错误"
        });
    }
    yield service.redisClients.nonceClient.multi().set(nonce, 1).pexpire(nonce, expireTime).exec();
    return yield Promise.resolve();
});
let checkAttach = co.wrap(function*(ipAddress) {
    let result = yield service.redisClients.attackClient.incr(ipAddress);
    if (result > 32) {
        yield exec("ufw deny from " + ipAddress + " to any");
        return Promise.reject();
    } else {
        yield service.redisClients.attackClient.multi().expire(ipAddress, 1).exec();
        return Promise.resolve();
    }
});
let wss;
let handleAttackLock = false;
let self = {
    startWebSocket: function() {
        return new Promise((resolve, reject) => {
            wss = new WebSocket.Server({
                port: 8888
            }, (err) => {
                if (err) reject(err);
                g.event.on("notifies", function(notifies) {
                    for (let notify of notifies) {
                        sendTo(notify.to, notify);
                    }
                });
                console.log("websocket启动成功");
                wss.on('connection', (ws) => {
                    const connectionId = ws._socket._handle.fd;
                    const remoteAddress = ws.upgradeReq.connection.remoteAddress;
                    ws._socket.setKeepAlive(true, 60 * 1000);
                    co(function*() {
                        let len = wss.clients.length;
                        if (len >= 500) {
                            if (!handleAttackLock) {
                                handleAttackLock = true;
                                yield self.stopWebSocket();
                                yield sleep(30 * 1000);
                                yield self.startWebSocket();
                                handleAttackLock = false;
                            }
                            return Promise.reject("服务器可能被攻击了，暂时关闭服务器");
                        }
                        service.logUserInfo(len);
                    }).catch((err) => {
                        console.log(err);
                    });
                    ws.on('message', (message) => {
                        co(function*() {
                            yield checkAttach(remoteAddress);
                            yield service.log({
                                ip: remoteAddress,
                                message: message
                            });
                            let json = JSON.parse(message);
                            let session = json.session;
                            let method = json.method;
                            yield checkParams(json.timestamp, json.nonce);
                            if (method == "createRoom") {
                                let opts = {
                                    birdCount: json.birdCount,
                                    maxGame: json.maxGame,
                                    maxBase: json.maxBase,
                                    mode: json.mode
                                };
                                let result = yield service.createRoom(session, opts);
                                sendTo(ws, result);
                            } else if (method == "wxLogin") {
                                let result = yield service.wxLogin(json.code, remoteAddress);
                                bindUnionIdWs(result.data.user.unionid, ws);
                                sendTo(ws, result);
                            } else if (method == "record") {
                                let result = yield service.record(session);
                                sendTo(ws, result);
                            } else if (method == "recordDetail") {
                                let result = yield service.recordDetail(session, json.recordId);
                                sendTo(ws, result);
                            } else if (method == "joinRoom") {
                                let result = yield service.joinRoom(session, json.roomNumber);
                                sendData(ws, result);
                            } else if (method == "reconnect") {
                                let result = yield service.reconnect(session, remoteAddress);
                                bindUnionIdWs(result.data.user.unionid, ws);
                                sendData(ws, result);
                            } else if (method == "dismissRoom") {
                                let result = yield service.dismissRoom(session);
                                sendData(ws, result);
                            } else if (method == "quitRoom") {
                                let result = yield service.quitRoom(session);
                                sendData(ws, result);
                            } else if (method == "daPai") {
                                let result = yield service.daPai(session, json.cardId);
                                sendData(ws, result);
                            } else if (method == "action") {
                                let result = yield service.action(session, json.actionId);
                                sendData(ws, result);
                            } else if (method == "zimo") {
                                let result = yield service.zimo(session, json.actionId);
                                sendData(ws, result);
                            } else if (method == "ting") {
                                let result = yield service.ting(session, json.actionId);
                                sendData(ws, result);
                            } else if (method == "anGang") {
                                let result = yield service.anGang(session, json.actionId);
                                sendData(ws, result);
                            } else if (method == "jiaGang") {
                                let result = yield service.jiaGang(session, json.actionId);
                                sendData(ws, result);
                            } else if (method == 'smallGameOk') {
                                let result = yield service.smallGameOk(session);
                                sendData(ws, result);
                            } else if (method == "voteDismissRoom") {
                                let result = yield service.voteDismissRoom(session, json.isAgreeDismiss);
                                sendData(ws, result);
                            } else if (method == "quikChat") {
                                let result = yield service.quikChat(session, json.index);
                                sendData(ws, result);
                            } else if (method == "setPassword") {
                                let result = yield service.setPassword(session, json.newPassword);
                                sendData(ws, result);
                            } else if (method == "sendSound") {
                                let result = yield service.sendSound(session, json.data);
                                sendData(ws, result);
                            } else if (method == "version") {
                                sendData(ws, {
                                    method: "version",
                                    data: {
                                        version: 1,
                                        url: "http://www.afmish.com/dgweb-113845.html"
                                    }
                                });
                            } else {
                                sendTo(ws, "method不存在");
                            }
                        }).catch((err) => {
                            if (err) {
                                service.log(err.stack);
                                sendTo(ws, err);
                            }
                        });
                    });
                    ws.on("close", function() {
                        unbindWs(ws);
                        service.logUserInfo(wss.clients.length);
                    });
                    ws.on("onerror", function(err) {
                        if (err) {
                            service.log(err.stack);
                            sendTo(ws, err);
                        }
                    });
                });
                resolve();
            });
        });
    },
    startServer: co.wrap(function*(devStatus) {
        devStatus = devStatus || g.DEV_STATUS_TEST;
        service = yield Service.create(devStatus);
        let db = yield service.initDb();
        schedule.scheduleJob("* * 0 * * *", function() {
            db.collection(g.colls.users).updateMany({
                roomCard: {
                    $lt: 10
                }
            }, {
                $set: {
                    roomCard: 10
                }
            });
        });
        console.log("mongodb连接成功");
        yield this.startWebSocket();
    }),
    stopWebSocket: function() {
        return new Promise((resolve, reject) => {
            wss.close((err) => {
                if (err) reject(err);
                resolve();
            });
        });
    }
};
module.exports = self;
